using System;
using System.Collections.Generic;
using System.Text;
using Curchy.PERTToolbox.Entity;

namespace Curchy.PERTToolbox.Service
{
    public class EstimationFactory
    {

        #region Singleton

        private static EstimationFactory _instance;

        private EstimationFactory() { }

        public static EstimationFactory Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new EstimationFactory();
                }
                return _instance;
            }
        }

        #endregion

        public Estimation GeneratePERT(Project project)
        {
            Estimation est = new Estimation();
            est.Project = project;
            est.DateTime = DateTime.Now;

            foreach (Activity task in project.Activities)
            {
                est.EstOptimistic = est.EstOptimistic.Add(FromCalendarToReal(task.TimeFrame, project.Calendar));
            }

            est.EstProbable = est.EstOptimistic;
            est.EstPessimistic = est.EstOptimistic;

            foreach (Risk risk in project.Risks)
            {
                est.EstProbable = est.EstProbable.Add(GetContengencyEstimate(FromCalendarToReal(risk.Contengency, project.Calendar), risk.RiskOccurance));
                est.EstPessimistic = est.EstPessimistic.Add(FromCalendarToReal(risk.Contengency, project.Calendar));
            }

            est.EstOptimistic = FromRealToCalendar(est.EstOptimistic, project.Calendar);
            est.EstProbable = FromRealToCalendar(est.EstProbable, project.Calendar);
            est.EstPessimistic = FromRealToCalendar(est.EstPessimistic, project.Calendar);

            return est;
        }

        private TimeSpan GetContengencyEstimate(TimeSpan ts, int occurance)
        {
            double totalSeconds = ts.TotalSeconds * occurance / 100;
            return  new TimeSpan(0, 0, Convert.ToInt32(totalSeconds));
        }

        private TimeSpan FromRealToCalendar(TimeSpan timeSpan, Calendar calendar)
        {
            int days = Convert.ToInt32(Math.Floor(timeSpan.TotalHours / calendar.HoursPerDay));
            double remHours = timeSpan.TotalHours - (days * calendar.HoursPerDay);
            int hours = Convert.ToInt32(Math.Floor(remHours));
            int mins = Convert.ToInt32(Math.Floor(remHours - hours)*60);

            return new TimeSpan(days, hours, mins, 0);
        }

        private TimeSpan FromCalendarToReal(TimeSpan timeSpan, Calendar calendar)
        {
            double totalNumberOfMinutes = (timeSpan.Days * calendar.HoursPerDay *60 ) + timeSpan.Hours * 60 + timeSpan.Minutes;
            int numberOfHours = Convert.ToInt32(Math.Floor(totalNumberOfMinutes / 60));
            int numberOfMin = Convert.ToInt32(totalNumberOfMinutes - (numberOfHours * 60));
            return new TimeSpan(numberOfHours, numberOfMin, 0);
        }

        public TimeSpan GeneratePERTAverage(Estimation estimation)
        {
            TimeSpan prb = FromCalendarToReal(estimation.EstProbable, estimation.Project.Calendar);
            TimeSpan opt = FromCalendarToReal(estimation.EstOptimistic, estimation.Project.Calendar);
            TimeSpan pes = FromCalendarToReal(estimation.EstPessimistic, estimation.Project.Calendar);
            TimeSpan fourTimes = prb.Add(prb).Add(prb).Add(prb);
            return FromRealToCalendar(DivideBySix(opt + fourTimes + pes), estimation.Project.Calendar);
        }

        public TimeSpan GeneratePERTStandardDeviation(Estimation estimation)
        {
            TimeSpan prb = FromCalendarToReal(estimation.EstProbable, estimation.Project.Calendar);
            TimeSpan opt = FromCalendarToReal(estimation.EstOptimistic, estimation.Project.Calendar);
            TimeSpan pes = FromCalendarToReal(estimation.EstPessimistic, estimation.Project.Calendar);
            TimeSpan fourTimes = prb.Add(prb).Add(prb).Add(prb);
            return FromRealToCalendar(DivideBySix(pes - opt), estimation.Project.Calendar);
        }

        private TimeSpan DivideBySix(TimeSpan ts)
        {
            return new TimeSpan(0, 0, Convert.ToInt32(ts.TotalSeconds / 6));
        }
    }
}
